<!--
* @author luguoxiang
* @date 2021-09-07
* 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
-->
<template>
  <div class="lg-table">
    <!-- 搜索栏 -->
    <div class="lg-table-search" :class="{ 'gl-height0': !searchShow }">
      <slot name="searchbar">
        <lg-search-bar
          v-if="searchbar"
          ref="searchRef"
          class="lg-table-search-bar"
          v-bind="searchbar"
          :schema="schema"
          @slot-change="searchSlotChange"
          @search="query"
          @reset="reset"
        >
          <!-- 搜索字段插槽 -->
          <template v-for="x in searchSlots" #[x]="scope">
            <slot :name="x" v-bind="scope"></slot>
          </template>
        </lg-search-bar>
      </slot>
    </div>
    <lg-auto-size class="lg-table-body">
      <!-- 工具栏 -->
      <slot name="toolbar">
        <div v-if="toolbar" class="lg-table-toolbar">
          <div class="lg-table-toolbar-left lg-space-r">
            <!--     左上角工具栏     -->
            <slot name="toolbar-left"></slot>
          </div>
          <div class="lg-table-toolbar-right lg-space-l">
            <!--     右上角工具栏     -->
            <slot name="toolbar-right" :reload="reload" :show-column-controller="showColumnController">
              <slot name="toolbar-right-prepend"></slot>
              <div class="table-controller lg-space-l">
                <template v-if="searchbar">
                  <span v-if="searchShow" title="隐藏搜索栏" class="lg-table-hover" @click="searchShow = false">
                    <search-icon style="color: #888" />
                  </span>
                  <span v-else title="显示搜索栏" class="lg-table-hover" @click="searchShow = true">
                    <search-icon />
                  </span>
                </template>
                <span title="刷新当前页" class="lg-table-hover" @click="reload()">
                  <refresh-icon />
                </span>
                <span
                  v-if="tableProps?.columnController"
                  title="列配置"
                  class="lg-table-hover"
                  @click="showColumnController"
                >
                  <bulletpoint-icon />
                </span>
              </div>
              <slot name="toolbar-right-append"></slot>
            </slot>
          </div>
        </div>
      </slot>
      <!-- 已选 -->
      <slot name="alert" :selected-keys="selectedKeys">
        <div v-if="selection && alert" class="lg-table-alert">
          <t-alert theme="info">
            <template v-if="selectedKeys.length">
              已选择 <span>{{ selectedKeys.length }}</span> 条数据
              <span class="lg-link" @click="clearSelection"> 清空</span>
            </template>
            <template v-else> 未选中任何数据 </template>
          </t-alert>
        </div>
      </slot>
      <!-- 表格 -->
      <enhanced-table
        :key="tableKey"
        ref="tableRef"
        v-model:columnControllerVisible="columnControllerVisible"
        :loading="loading"
        :row-key="rowKey"
        :columns="tableConfig.columns"
        :data="dataList"
        :selected-row-keys="selectedKeys"
        v-bind="tableProps"
        size="small"
        @sort-change="sortChange"
        @select-change="selectionChange"
      >
        <template v-for="x in tableConfig.slots" #[x]="scope">
          <slot :name="x" v-bind="scope"></slot>
        </template>
        <template v-for="x in tableConfig.dictSlots" #[x.field]="{ row }">
          <div
            v-if="row[x.field] !== null && row[x.field] !== undefined"
            class="lg-table-dict"
            :class="['lg-table-dict-' + x.code, 'dict-value-' + row[x.field]]"
          >
            {{ dict[x.code][row[x.field]] }}
          </div>
        </template>
      </enhanced-table>
      <div v-if="pagination" class="lg-table-pagination">
        <t-pagination v-model="pagination.current" v-bind="pagination" @change="pageChange" />
      </div>
    </lg-auto-size>
  </div>
</template>

<script lang="ts" setup>
import './index.less';
import { computed, ref, useAttrs, watch, watchEffect } from 'vue';
import { BaseTableCol, PaginationProps, EnhancedTable, TableProps } from 'tdesign-vue-next';
import { SearchIcon, RefreshIcon, BulletpointIcon } from 'tdesign-icons-vue-next';
import {
  IConfigSchema,
  ITableSchema,
  LgSearchBar,
  toAsyncLoader,
  useDictTextBatch,
  LgAutoSize,
  onCellClickCopy,
} from '@/soar';
import { LgTableProps, LgSearchBarProps } from '../types';
import { defaultPageSize } from '@/config/global';
import { getDefault, handleColumns } from './utils';

interface Props<T> extends LgTableProps<T> {
  /**
   * 多列配置
   */
  schema: ITableSchema<T>[] | IConfigSchema<T>[];
  /**
   * 数据配置
   */
  data?: Function | string | T[];
  /**
   * 查询字段配置
   */
  fields?: string | string[];
  /**
   * 默认排序字段（优先级低于列配置）
   */
  orderBys?: string | string[];
  /**
   * 搜索栏配置
   */
  searchbar?: LgSearchBarProps<T> | boolean;
  /**
   * 工具栏
   */
  toolbar?: boolean;
  /**
   * 已选栏
   */
  alert?: boolean;
  /**
   * 分页配置
   */
  pagination?: PaginationProps | boolean;
  /**
   * 列默认配置
   */
  column?: BaseTableCol;
  /**
   * 多选框列
   */
  selection?: BaseTableCol | boolean;
  /**
   * 序号列
   */
  index?: BaseTableCol | boolean;
  /**
   * 操作列
   */
  actionbar?: BaseTableCol | boolean;
  /**
   * 是否立即加载数据
   */
  immediate?: boolean;
  /**
   * id字段
   */
  rowKey?: string;
  /**
   * t-table 配置
   */
  tableProps?: TableProps;
  /**
   * 参数处理
   * @param soarQueryParams 经过 soarQuery 处理的参数
   * @param raw 原始参数
   */
  paramsHandle?(soarQueryParams: any, raw?: any): unknown;
  /**
   * 数据处理
   * @param data
   */
  dataHandle?(data: any): unknown;
  /**
   * 实例引用
   */
  ref?(e: any): void;
}

const props = withDefaults(defineProps<Props<unknown>>(), {
  rowKey: 'id',
  fields: [],
  orderBys: [],
  toolbar: true,
  alert: true,
  immediate: true,
});
const emit = defineEmits(['sort-change', 'page-change', 'select-change']);

const attrs = useAttrs();
// 列控制显示
const columnControllerVisible = ref(false);
function showColumnController() {
  columnControllerVisible.value = true;
}

const tableRef = ref();
// 数据加载中
const loading = ref(false);
// 是否显示搜索兰
const searchShow = ref(true);
// 排序字段
const orderBy = ref([]);
// 表格数据
const dataList = ref([]);
// 生成表格配置数据
const tableConfig = computed(() => handleColumns(props));
const tableProps = computed(() => {
  return {
    resizable: true,
    bordered: true,
    hover: true,
    scroll: { type: 'lazy', bufferSize: 20 },
    columnController: { hideTriggerButton: true, displayType: 'fixed-width' },
    onCellClick: onCellClickCopy,
    ...attrs,
    ...props.tableProps,
  };
});
// 数据字典
const dict = computed(() => useDictTextBatch(tableConfig.value.dictSlots.map((x) => x.code)));
// 分页数据
const pagination = ref<PaginationProps>();
watch(
  () => props.pagination,
  (temp) => {
    if (temp) {
      pagination.value = getDefault(temp, {
        current: 1,
        showJumper: true,
        defaultPageSize,
        pageSizeOptions: [10, 20, 50, 100, 200, 500],
        disableDataPage: true,
      } as PaginationProps);
    } else {
      pagination.value = null;
    }
  },
  { immediate: true },
);
// 分页参数
const pageInfo = { pageNo: 1, pageSize: 10 };
watchEffect(() => {
  if (pagination.value) {
    pageInfo.pageNo = pagination.value.current ?? pagination.value.defaultCurrent;
    pageInfo.pageSize = pagination.value.pageSize ?? pagination.value.defaultPageSize;
  }
});

// 查询参数
const searchData: { params: any; soarQuery: any } = { params: {}, soarQuery: {} };
function getParams() {
  return {
    fields: [...tableConfig.value.fields],
    orderBys: [...orderBy.value, ...tableConfig.value.defaultOrderBys],
    params: { ...searchData.params },
  };
}
// 获取参数
function getSoarQuery(data: any = getParams()) {
  // soarQuery 参数处理
  return {
    fields: data.fields,
    orderBys: data.orderBys,
    ...searchData.soarQuery,
  };
}
// 加载数据
function reload(pageNo?: number): void {
  // 页码处理
  if (pageNo) {
    pageInfo.pageNo = pageNo;
    if (pagination.value) {
      pagination.value.current = pageNo;
    }
  }
  const pageParams = pagination.value ? pageInfo : { queryType: 'list', pageSize: 999999 };
  // 其他参数
  const params = getParams();
  // soarQuery 参数
  let soarQueryParams = { $soarQuery: getSoarQuery(params), ...pageParams };
  // 发起请求
  if (typeof props.paramsHandle === 'function') {
    const p = props.paramsHandle(soarQueryParams, { ...params, page: { ...pageParams } });
    if (p) {
      soarQueryParams = p;
    }
  }
  toAsyncLoader(props.data)(soarQueryParams)
    .then((res) => {
      if (typeof props.dataHandle === 'function') {
        const d = props.dataHandle(res);
        if (d) {
          res = d;
        }
      }
      res ??= [];
      if (Array.isArray(res)) {
        res = { list: res, total: res.length };
      }
      dataList.value = res.list;
      if (pagination.value) {
        pagination.value.total = res.total;
      }
      renderTable();
    })
    .finally(() => {
      loading.value = false;
    });
  loading.value = true;
}

// 监听table排序变化
function sortChange(e: any, b: any) {
  emit('sort-change', e, b);
  if (e) {
    const orderByField: string = (e.descending ? '-' : '') + tableConfig.value.orderMap[e.sortBy];
    orderBy.value = [orderByField];
  } else {
    orderBy.value = [];
  }
  reload();
}
// 监听分页变化
function pageChange(params: { current: number; pageSize: number }) {
  emit('page-change', params);
  pageInfo.pageNo = params.current;
  pageInfo.pageSize = params.pageSize;
  reload();
}

// 监听table多选框变变化 ==========================================================================================
// 选择的数据
const selectedKeys = ref<any[]>([]);
const selectedRows = ref<any[]>([]);
function selectionChange(a: any[], b: { selectedRowData: any[] }) {
  emit('select-change', a, b);
  selectedKeys.value = a;
  selectedRows.value = b.selectedRowData;
}
// 清空用户选择
function clearSelection() {
  selectedKeys.value = [];
  selectedRows.value = [];
}
// 选择
function select(keys: any[], overwrite = true) {
  if (!Array.isArray(keys)) {
    keys = [keys];
  }
  if (overwrite) {
    selectedKeys.value = keys;
  } else {
    for (const key of keys) {
      if (!selectedKeys.value.includes(key)) {
        selectedKeys.value.push(key);
      }
    }
  }
}
// 取消选择
function deselect(keys: any[]) {
  if (!Array.isArray(keys)) {
    keys = [keys];
  }
  for (const key of keys) {
    const index = selectedKeys.value.indexOf(key);
    if (index !== -1) {
      selectedKeys.value.splice(index, 1);
    }
  }
}
// 加载数据
watch(
  () => props.data,
  (v) => {
    reload(1);
  },
  { immediate: props.immediate },
);

// 监听搜索栏的插槽变化 ==========================================================================================
const searchRef = ref();
// 搜索栏
const searchbar = computed(() => {
  const temp = props.searchbar;
  if (temp) {
    return getDefault(temp, {});
  }
  return null;
});
const searchSlots = ref([]);
function searchSlotChange(slots: string[]) {
  searchSlots.value = slots;
}
// 监听搜索栏查询事件
function query(values: any, sq: any): void {
  searchData.params = values;
  searchData.soarQuery = sq;
  reload(1);
}
// 监听搜索栏重置事件
function reset(values: any, sq: any): void {
  searchData.params = values;
  searchData.soarQuery = sq;
  reload(1);
}

const tableKey = ref(0);
function renderTable() {
  tableKey.value = Math.random();
}

// 对外暴露的api
defineExpose({
  searchRef,
  tableRef,
  selectedKeys,
  selectedRows,
  dataList,
  getParams,
  getSoarQuery,
  reload,
  reset,
  clearSelection,
  select,
  deselect,
  forceRerender: renderTable,
  validateRowData(rowValue: any) {
    return tableRef.value?.validateRowData(rowValue);
  },
  validateTableData() {
    return tableRef.value?.validateTableData();
  },
});
</script>
<script lang="ts">
export default {
  name: 'LgTable',
};
</script>
